/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jj2000.stg.wt;

import jj2000.j2k.image.DataBlkInt;
import jj2000.j2k.image.input.ImgReaderPPM;

/**
 *
 * @author teq
 */
public class HaarWT {
    
    private HaarWT parent = null;
    private HaarWT child = null;
    private DataBlkInt decomposed = null;

    public HaarWT(DataBlkInt pSrc, int pDepth) {
        this(null, pSrc, pDepth);
    }
    
    public HaarWT(HaarWT pParent, DataBlkInt pSrc, int pDepth) {
        
        parent = pParent;
        
        // Forward wavelet transform
        
        int x,y,a,i,k;

        DataBlkInt d = new DataBlkInt(0, 0, pSrc.w, pSrc.h);
        
        for (y = 0; y < d.h; y++) {
            for (x = 0; x < d.w / 2; x++) {
                i = y * pSrc.w + 2 * x;
                a = (pSrc.data[i] + pSrc.data[i + 1]) / 2;
                k = y * d.w + x;
                d.data[k] = a;
                d.data[k + d.w / 2] = a - pSrc.data[i];
            }
        }
        
        DataBlkInt e = new DataBlkInt(0, 0, pSrc.w, pSrc.h);
        
        for (x = 0; x < e.w; x++) {
            for (y = 0; y < e.h / 2; y++) {
                i = 2 * y * d.w + x;
                a = (d.data[i] + d.data[i + d.w]) / 2;
                k = y * e.w + x;
                e.data[k] = a;
                e.data[k + e.w * e.h / 2] = a - d.data[i];
            }
        }
        
        if (pDepth > 1) {
            child = new HaarWT(this, getSubBlk(e, 0, 0, e.w / 2, e.h / 2), pDepth - 1);
        }
        
        decomposed = e ;
    }
    
    private static DataBlkInt getSubBlk(DataBlkInt pSrc,
                                        int pX, int pY,
                                        int pW, int pH) {
        
        DataBlkInt result = new DataBlkInt(0, 0, pW, pH);
        
        int i = 0;
        for (int y = pY; y < pY + pH; y++) {
            for (int x = pX; x < pX + pW; x++) {
                result.data[i++] = pSrc.data[y * pSrc.w + x];
            }
        }
        
        return result;
    }
    
    private static DataBlkInt getPaintedBlk(DataBlkInt pBackg,
                                            DataBlkInt pForeg,
                                            int pX, int pY) {
        
        DataBlkInt result = new DataBlkInt(pBackg);
        
        int i = 0;
        for (int y = 0; y < pForeg.h; y++) {
            for (int x = 0; x < pForeg.w; x++) {
                result.data[result.w * (pY + y) + pX + x] = pForeg.data[i++];
            }
        }
        
        return result;
    }
    
    public void applyStegToHH(DataBlkInt pSteg, int bits)
    {
        if (bits < 1 || bits > 8) {
            return;
        }
        
        int dx,dy,x,y;
        byte val;
        
        for (dy = decomposed.h / 2, y = 0; dy < decomposed.h && y < pSteg.h; dy++, y++) {
            for (dx = decomposed.w / 2, x = 0; dx < decomposed.w && y < pSteg.w; dx++, x++) {
                val = (byte)(decomposed.data[dy * decomposed.w + dx] + 128);
                val &= (0xFF << bits); // mask (logic and)
                val |= (pSteg.data[y * pSteg.w + x] + ImgReaderPPM.DC_OFFSET) >> (8 - bits); // logic or
                decomposed.data[dy * decomposed.w + dx] = (val - 128) & 0xFF;
            }
        }
    }
    
    public HaarWT getParent() {
        return parent;
    }
    
    public HaarWT getChild() {
        return child;
    }

    public DataBlkInt getSourceImg() {
        
        // Reverse wavelet transform
        
        int x,y,i,k,r,l;

        DataBlkInt d = new DataBlkInt(getDecomposedImg());
        if (child != null) {
            d = getPaintedBlk(d, child.getSourceImg(),0 , 0);
        }

        DataBlkInt e = new DataBlkInt(0, 0, d.w, d.h);
        
        for (x = 0; x < e.w; x++) {
            for (y = 0; y < e.h / 2; y++) {
                i = y * d.w + x;
                l = d.data[i] - d.data[i + d.w * d.h / 2];
                r = d.data[i] + d.data[i + d.w * d.h / 2];
                k = 2 * y * e.w + x;
                e.data[k] = l;
                e.data[k + e.w] = r;
            }
        }

        DataBlkInt f = new DataBlkInt(0, 0, e.w, e.h);
        
        for (y = 0; y < f.h; y++) {
            for (x = 0; x < f.w / 2; x++) {
                i = y * e.w + x;
                l = e.data[i] - e.data[i + e.w / 2];
                r = e.data[i] + e.data[i + e.w / 2];
                k = y * f.w + 2 * x;
                f.data[k] = l;
                f.data[k + 1] = r;
            }
        }
        
        return f;
    }
    
    public DataBlkInt getDecomposedImg() {
        if(child == null) {
            return decomposed;
        } else {
            return getPaintedBlk(decomposed, child.getDecomposedImg(), 0, 0);
        }
    }
    
    public static int getMinValue(DataBlkInt blk) {
        
        if (blk.data.length == 0){
            return 0;
        }
        
        int min = blk.data[0];
        
        for(int i = 0; i < blk.data.length; i++) {
            min = (blk.data[i] < min)? blk.data[i] : min;
        }
        
        return min;
    }
    
    public static int getMaxValue(DataBlkInt blk) {
        
        if (blk.data.length == 0){
            return 0;
        }
        
        int max = blk.data[0];
        
        for(int i = 0; i < blk.data.length; i++) {
            max = (blk.data[i] > max)? blk.data[i] : max;
        }
        
        return max;
    }
}
